package net.cl.tools;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.cl.utils.HtmlUtil;

public class AntiXSS {

	private static final String[] events = { "onmouseover", "onmouseout", "onmousedown", "onmouseup", "onmousemove",
			"onclick", "ondblclick", "onkeypress", "onkeydown", "onkeyup", "ondragstart", "onerrorupdate", "onhelp",
			"onreadystatechange", "onrowenter", "onrowexit", "onselectstart", "onload", "onunload", "onbeforeunload",
			"onblur", "onerror", "onfocus", "onresize", "onscroll", "oncontextmenu" };
	private static final String[] protocols = { "javascript", "vbscript", "livescript", "ms-its", "mhtml", "data",
			"firefoxurl", "mocha" };

	public static String antiXSS(String content) {
		String old = content;
		String ret = _antiXSS(content);
		while (!ret.equals(old)) {
			old = ret;
			ret = _antiXSS(ret);
		}
		return ret;
	}

	private static String _antiXSS(String content) {
		try {
			return stripAllowScriptAccess(stripProtocol(stripCssExpression(stripAsciiAndHex(stripEvent(stripScriptTag(content))))));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private static String stripScriptTag(String content) {
		return HtmlUtil.stripScriptTag(content);
	}

	private static String stripEvent(String content) {
		StringBuffer sb = null;
		for (String event : events) {
			sb = new StringBuffer();
			Pattern pattern = Pattern.compile("(<[^>]*)(" + event + ")([^>]*>)", Pattern.CASE_INSENSITIVE);
			Matcher matcher = pattern.matcher(content);
			while (matcher.find())
				matcher.appendReplacement(sb, "$1" + matcher.group(2).substring(2) + "$3");
			matcher.appendTail(sb);
			content = sb.toString();
		}
		return content;
	}

	/**
	 * filter &# \00xx
	 * 
	 * @param content
	 * @return
	 */
	private static String stripAsciiAndHex(String content) {
		StringBuffer sb = new StringBuffer();
		Pattern pattern = Pattern.compile("(<[^>]*)(&#|\\\\00)([^>]*>)", Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(content);
		while (matcher.find())
			matcher.appendReplacement(sb, "$1$3");
		matcher.appendTail(sb);
		return sb.toString();
	}

	private static String stripCssExpression(String content) throws Exception {
		StringBuffer sb = new StringBuffer();
		Pattern pattern = Pattern.compile("(<\\s*style=.*)/\\*.*\\*/([^>]*>)", Pattern.CASE_INSENSITIVE
				| Pattern.DOTALL);
		Matcher matcher = pattern.matcher(content);
		while (matcher.find())
			matcher.appendReplacement(sb, "$1$2");
		matcher.appendTail(sb);
		content = sb.toString();
		sb = new StringBuffer();
		pattern = Pattern.compile("(<\\s*style=[^>]+)(expression|javascript|vbscript|-moz-binding)([^>]*>)",
				Pattern.CASE_INSENSITIVE);
		matcher = pattern.matcher(content);
		while (matcher.find())
			matcher.appendReplacement(sb, "$1$3");
		matcher.appendTail(sb);
		content = sb.toString();
		sb = new StringBuffer();
		pattern = Pattern.compile("(<\\s*style[^>]*>.*)/\\*.*\\*/(.*<\\s*/style[^>]*>)", Pattern.CASE_INSENSITIVE
				| Pattern.DOTALL);
		matcher = pattern.matcher(content);
		while (matcher.find())
			matcher.appendReplacement(sb, "$1$2");
		matcher.appendTail(sb);
		content = sb.toString();
		sb = new StringBuffer();
		pattern = Pattern.compile(
				"(<\\s*style[^>]*>[^>]+)(expression|javascript|vbscript|-moz-binding)(.*<\\s*/style[^>]*>)",
				Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
		matcher = pattern.matcher(content);
		while (matcher.find())
			matcher.appendReplacement(sb, "$1$3");
		matcher.appendTail(sb);
		content = sb.toString();
		return content;
	}

	private static String stripProtocol(String content) throws Exception {
		StringBuffer sb = null;
		for (String protocol : protocols) {
			sb = new StringBuffer();
			Pattern pattern = Pattern.compile("(<[^>]*)" + protocol + ":([^>]*>)", Pattern.CASE_INSENSITIVE);
			Matcher matcher = pattern.matcher(content);
			while (matcher.find())
				matcher.appendReplacement(sb, "$1/$2");
			matcher.appendTail(sb);
			content = sb.toString();
		}
		return content;
	}

	private static String stripAllowScriptAccess(String content) throws Exception {
		StringBuffer sb = new StringBuffer();
		Pattern pattern = Pattern.compile("(<[^>]*)AllowScriptAccess([^>]*>)", Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(content);
		while (matcher.find())
			matcher.appendReplacement(sb, "$1Allow_Script_Access$2");
		matcher.appendTail(sb);
		return sb.toString();
	}

}